/** @file    DoorBell.cpp
 *  @author  Alessandro Polo
 *  @version $Id: DoorBell.cpp 2829 2010-08-02 20:45:47Z alex $
 *  @brief
 * File containing methods for the wosh::building::DoorBell class.
 * The header for this class can be found in DoorBell.h, check that file
 * for class description.
 ****************************************************************************/
/* Copyright (c) 2007-2010, WOSH - Wide Open Smart Home 
 * by Alessandro Polo - OpenSmartHome.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the OpenSmartHome.com WOSH nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Alessandro Polo ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Alessandro Polo BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ****************************************************************************/
 
 #include <framework/building/DoorBell.h>

 #include <core/ObjectAllocator.h>
 #include <core/MethodsCommon.h>
 #include <core/List.h>


namespace wosh {
 namespace building {

 WOSH_REGISTER(wosh::building::DoorBell, "wosh::building::BuildingObject", _DoorBell_VERSION, _static_DoorBell )

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS

DoorBell::DoorBell()
	: BuildingObject() {
	BuildingObject::setName( "wosh.building.DoorBell", _GET_Abs(Utilities::randomLong()) );
 }

void DoorBell::init() {
	Log(LOG_DEBUG, " Setting default properties and permissions.." );

	this->state = DoorBell::BELL_UNKNOWN;
	setState(DoorBell::BELL_UNKNOWN, false);

	Log(LOG_DEBUG, " Registering methods.." );
	MethodWRESULT* mmMute = Methods.create<MethodWRESULT>( _DoorBell_METHOD_mute, "mute the bell", Permission::R_XR_XR__() );
	mmMute->setMethod( this, (MethodWRESULTPtr)&DoorBell::mute );

	MethodWRESULT* mmTest = Methods.create<MethodWRESULT>( _DoorBell_METHOD_test, "test the door bell", Permission::R_XR_XR__() );
	mmTest->setMethod( this, (MethodWRESULTPtr)&DoorBell::test );

	BuildingObject::init();
 }

//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////// PROCESS CHILD MESSAGES

void DoorBell::busMessageChild( const Message& childMessage ) {
	if ( childMessage.isEmpty() ) return;

	if ( childMessage.getContent()->isFact() ) {
		const Fact* fact = childMessage.getContent()->asFact();
		if ( fact == NULL ) return;
		if ( fact->getEvent() == "StateChanged" ) {

			if ( !fact->hasData() ) {
				Log(LOG_WARNING, ":busMessageChild() EVENT 'StateChanged' Invalid, no Content!" );
				return;
			 }
			const Variant* data = NULL;
			if ( fact->getData()->isKindOf<Variant>() )
				data = dynamic_cast<const Variant*>( fact->getData() );
			if ( data == NULL ) {
				Log(LOG_WARNING, ":busMessageChild() EVENT 'StateChanged' Invalid, Not Data-Content!" );
				return;
			 }
			int value = data->toInteger(-1);
			Log(LOG_INFO, ":busMessageChild() EVENT 'StateChanged' => %d [%s]", value, data->getTypeAsString() );

			if ( value == 0 )
				setState(DoorBell::BELL_IDLE, true, childMessage.getSecurityToken());
			else if ( value != -1 )
				setState(DoorBell::BELL_RINGING, true, childMessage.getSecurityToken());
		 }
		else
			Log(LOG_WARNING, ":busMessageChild() UNKNOWN EVENT '%s'", fact->getEvent().c_str() );

	 }
	else
		Log(LOG_INFO, ":busMessageChild()" );

 }

////////////////////////////////////////////////////////////////////////////////////// PROCESS CHILD MESSAGES
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////// GENERATED EVENTS

void DoorBell::setState( DoorBell::BELL_STATE new_state, bool raise_event, const SecurityToken* source_token ) {
	DoorBell::BELL_STATE old_state = this->state;
	Properties.transactionBeginWrite();
	if ( this->state != new_state ) {
		this->state = new_state;
		Properties.update( _BuildingObject_KEY_State, getStateAsString() );
	 }
	Properties.update( _BuildingObject_KEY_StateTs, Variant(Utilities::std_time(), Variant::DATETIME) );
	Properties.transactionEnd();
	if ( this->raiseAlwaysEvents || raise_event ) {
		Log(LOG_VERBOSE, ":setState(%s) Raising Event.. ", getStateAsString() );
		BuildingObject::raiseEvent( new DoorBell_Ringing(this->state, old_state), source_token );
	 }
	else
		Log(LOG_DEBUG, ":setState(%s) Not Raising Event.", getStateAsString() );
 }

//////////////////////////////////////////////////////////////////////////////////////////// GENERATED EVENTS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////// DEVICE METHODS

WRESULT DoorBell::mute() {
	Log(LOG_WARNING, ":mute() : Raising IDLE event (Forced).." );
	setState(DoorBell::BELL_IDLE, true);
	return WRET_OK;
 }

WRESULT DoorBell::test() {
	Log(LOG_WARNING, ":test() : Raising RINGING event (Forced).." );
	setState(DoorBell::BELL_RINGING, true);
	return WRET_OK;
 }

////////////////////////////////////////////////////////////////////////////////////////////// DEVICE METHODS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

bool DoorBell::updateHash( DataHashContext* hash_context ) const {
	if ( hash_context == NULL ) return false;
	bool ret = BuildingObject::updateHash( hash_context );
	ret = hash_context->update( this->state ) & ret;
	return ret;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

const char* DoorBellStateStr[] = {
	"UNKNOWN",	// (BELL_STATE)0
	"IDLE",		// (BELL_STATE)1
	"RINGING"	// (BELL_STATE)2
 };

Enum2StrFromArray(DoorBell::getStateAsString, DoorBell::BELL_STATE, BELL_IDLE, BELL_RINGING, 0, DoorBellStateStr)

Str2EnumByArray(DoorBell::getStateFromString, DoorBell::BELL_STATE, 0, BELL_RINGING, BELL_UNKNOWN, DoorBellStateStr)

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

DoorBell::BELL_STATE intToDoorBellStateEnum( int value ) {
	if ( value <= DoorBell::BELL_UNKNOWN ) return DoorBell::BELL_UNKNOWN;
	if ( value > DoorBell::BELL_RINGING ) return DoorBell::BELL_UNKNOWN;
	return (DoorBell::BELL_STATE)value;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

 WOSH_REGISTER(wosh::building::DoorBell_Ringing, "wosh::Fact", 1.0, _static_DoorBell_Ringing )

DoorBell_Ringing::DoorBell_Ringing( DoorBell::BELL_STATE current_doorbell_state )
	: wosh::Fact( _Door_EVENT_DoorBellRinging ) {
	setData( new Variant(current_doorbell_state) );
 }

DoorBell_Ringing::DoorBell_Ringing( DoorBell::BELL_STATE current_doorbell_state, DoorBell::BELL_STATE previous_doorbell_state )
	: wosh::Fact( _Door_EVENT_DoorBellRinging ) {
	List* args = List::createFrom<Variant>( (int)current_doorbell_state, (int)previous_doorbell_state );
	setData(args);
 }

DoorBell::BELL_STATE DoorBell_Ringing::getDoorBellState() const {
	const Variant* var = getDataItem<Variant>(0);
	if ( var == NULL ) return DoorBell::BELL_UNKNOWN;
	if ( var->isNumeric() ) return DoorBell::BELL_UNKNOWN;
	return intToDoorBellStateEnum(var->toInteger( (int)DoorBell::BELL_UNKNOWN ));
 }

DoorBell::BELL_STATE DoorBell_Ringing::getPreviousDoorBellState() const {
	const Variant* var = getDataItem<Variant>(1);
	if ( var == NULL ) return DoorBell::BELL_UNKNOWN;
	if ( var->isNumeric() ) return DoorBell::BELL_UNKNOWN;
	return intToDoorBellStateEnum(var->toInteger( (int)DoorBell::BELL_UNKNOWN ));
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////




 }; // namespace building
}; // namespace wosh
